//  -----------------------------------------------------
//  Project RenderIt
//  Contact : hzzhoukeli@corp.netease.com
//  Author : zhoukeli
//  -----------------------------------------------------
/// @file
/// @path
/// @data 2016/05/04
/// @brief

#pragma once

#include "public/build.h"

namespace renderit
{
	namespace math
	{
		class vector3
		{
		public:
			vector3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
			vector3() : x(0), y(0), z(0) {}
			vector3(float v) : x(v), y(v), z(v) {}
			vector3(const vector3& rh)
			{
				this->operator=(rh);
			}

		public:
			vector3 & operator = (const vector3 & rh)
			{
				if (&rh != this) {
					x = rh.x;
					y = rh.y;
					z = rh.z;
				}

				return *this;
			}

			float & operator [] (int i)
			{
				return v_[i];
			}

			const float & operator [] (int i) const
			{
				return v_[i];
			}

			vector3 & operator += (const vector3 & rh)
			{
				if (&rh != this) {
					x += rh.x;
					y += rh.y;
					z += rh.z;
				}
				return *this;
			}

			vector3 & operator -= (const vector3 & rh)
			{
				if (&rh != this) {
					x -= rh.x;
					y -= rh.y;
					z -= rh.z;
				}
				return *this;
			}

			vector3 & operator *= (float v)
			{
				x *= v;
				y *= v;
				z *= v;
				return *this;
			}

			vector3 & operator /= (float v)
			{
				if (v) {
					x /= v;
					y /= v;
					z /= v;
				}
				return *this;
			}

			vector3 operator - (const vector3 & rh) const
			{
				vector3 v(*this);
				v -= rh;
				return v;
			}

			vector3 operator + (const vector3 & rh) const
			{
				vector3 v(*this);
				v += rh;
				return v;
			}

			vector3 operator * (float f) const
			{
				vector3 v(*this);
				v *= f;
				return v;
			}

			vector3 operator / (float f) const
			{
				vector3 v(*this);
				v /= f;
				return v;
			}

			friend vector3 operator * (float f, const vector3 & v);

		public:
			float dot(const vector3 & rh) const
			{
				return x * rh.x + y * rh.y + z * rh.z;
			}

			vector3 & cross_in_place(const vector3 & rh)
			{
				float x_ = y * rh.z - z * rh.y;
				float y_ = z * rh.x - x * rh.z;
				float z_ = x * rh.y - y * rh.x;
				x = x_;
				y = y_;
				z = z_;
				return *this;
			}

			vector3 cross(const vector3 & rh) const
			{
				vector3 v(*this);
				v.cross_in_place(rh);
				return v;
			}

			float length2() const
			{
				return x * x + y * y + z * z;
			}

			float length() const
			{
				return sqrt(this->length2());
			}

			void normalize()
			{
				float len = length();
				if (len > 0) {
					x /= len;
					y /= len;
					z /= len;
				}
			}

		public:
			union {
				struct {
					float x, y, z;
				};
				float v_[3];
			};
		};

		inline vector3 operator * (float f, const vector3 & v)
		{
			return v * f;
		}
	}
}
